package com.saui.traceu;


import java.util.ArrayList;
import java.util.List;

import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.drawable.Drawable;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.text.Editable;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapView;
import com.google.android.maps.MyLocationOverlay;
import com.google.android.maps.Overlay;
import com.google.android.maps.Projection;
import com.saui.traceu.controller.LocationHelper;
import com.saui.traceu.controller.SessionManager;
import com.saui.traceu.controller.TraceCache;
import com.saui.traceu.model.TraceDBAdapter;
import com.saui.traceu.view.TraceLiveOverlay;

public class TraceMap extends MapActivity {

	Paint mPaint;
	Path path;
	Projection projection;

	protected static final int PAUSE_DIALOG = 1;
	private static final boolean DEBUGGING = true;

	public static final int RECORD_ID = Menu.FIRST;
	public static final int LIST_ID = Menu.FIRST+1;
	private boolean recording = false;

	List<Overlay> mapOverlays;
	Drawable drawable;

	// Important objects to keep around; database management, session management, location service, etc.
	TraceDBAdapter mDb;
	SessionManager sessionManager;
	LocationHelper locationHelper;
	TraceCache traceCache;

	TraceLiveOverlay liveOverlay;

	EditText traceNameEditText;

	
	/**
	 * 
	 * @param text String to toast to screen
	 */
	public void printTester(String text){
		Toast.makeText(this,text,Toast.LENGTH_LONG).show();
	}

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		try {
			this.onCreateHelper(savedInstanceState);
		} catch (Exception e) {
			if (DEBUGGING){this.printTester("onCreate "+e.toString());}
			else {e.printStackTrace();}
		}
	}

	
	/**
	 * A method to facilitate easier debugging of onCreate.
	 * 
	 * @param savedInstanceState the same parameter passed to onCreate
	 */
	private void onCreateHelper(Bundle savedInstanceState) {

		// Show the Map
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);   
		MapView mapView = (MapView) findViewById(R.id.mapview);
		mapView.setBuiltInZoomControls(true);
		projection = mapView.getProjection();

		// Setup overlays
		mapOverlays = mapView.getOverlays();

		MyLocationOverlay myLocationOverlay = new MyLocationOverlay(this, mapView);
		mapView.getOverlays().add(myLocationOverlay);
		myLocationOverlay.enableMyLocation();
		liveOverlay = new TraceLiveOverlay(new ArrayList<GeoPoint>(), Color.RED);
		mapView.getOverlays().add(liveOverlay);

		// get location and begin getting location updates
		String serviceString = Context.LOCATION_SERVICE;  
		LocationManager locationManager = (LocationManager)getSystemService(serviceString);   
		locationHelper = LocationHelper.getLocationHelper(this.getLocationListener(),locationManager);
		Location location = locationHelper.getLatestLocation();
		this.updateLocation(location);
		if (location != null)
			mapView.getController().setCenter(new GeoPoint((int)(location.getLatitude()*1000000),
					(int)(location.getLongitude()*1000000)));

		// Get Database adapter for later use
		mDb = new TraceDBAdapter(this);
		mDb.open();
		// Delete any let over recording sessions from crashes, etc.
		mDb.deleteUnsavedSessions();

		//Get SessionManager to handle current Session state and displayed sessions.
		sessionManager = SessionManager.getSessionManager();

		// Get Trace Cache to handle storing and saving location traces.
		traceCache = TraceCache.getTraceCache(mDb,sessionManager);

	}

	
	/** Have to Override this method for MapView to work */
	@Override
	protected boolean isRouteDisplayed() {return false;}

	//----------------------------------------//
	//  Code for Handling Pause Dialog		  //
	//----------------------------------------//
	/**
	 * Handle preparation of the Pause dialog
	 */
	@Override
	public void onPrepareDialog(int id, Dialog d) {
		switch(id){
		case TraceMap.PAUSE_DIALOG:
			EditText traceName = (EditText)d.findViewById(R.id.trace_save_name);
			traceName.setText(sessionManager.getCurrentSession().getTitle());
			traceName.selectAll();
			break;
		}
	}

	
	/**
	 *  Handle creation/display of the pause dialog.
	 *  
	 */
	@Override
	public Dialog onCreateDialog(int id) {
		Dialog d = null;
		switch(id){
		case TraceMap.PAUSE_DIALOG:
			d = new Dialog(TraceMap.this);
			Window w = d.getWindow();
			w.setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND, 
					WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
			d.setTitle("Recording Paused");
			d.setContentView(R.layout.layout_pause);

			//set text edit text for trace name
			traceNameEditText = (EditText)d.findViewById(R.id.trace_save_name);
			traceNameEditText.setText(sessionManager.getCurrentSession().getTitle());

			saveButtonHelper(d); // setUp save button
			continueButtonHelper(d); // setUp continue button
			stopButtonHelper(d); // setup stop button

			d.setOnDismissListener(new OnDismissListener(){
				public void onDismiss(DialogInterface arg0) {
					if (recording)
						sessionManager.getCurrentSession().setTitle(traceNameEditText.getText().toString());
				}
			});
			break;
		}
		return d;
	}


	/**
	 * Helper method for setting up the Stop/cancel recording button on the pause dialog.
	 * @param d the Dialog object for the pUSE DIALOG
	 */
	private void stopButtonHelper(Dialog d) {
		Button stopButton = (Button)d.findViewById(R.id.stop_button);
		stopButton.setOnClickListener(new Button.OnClickListener(){
			public void onClick(View v) {
				recording = false;
				locationHelper.pauseLocationUpdates();
				liveOverlay.clearPoints();
				String name = sessionManager.getCurrentSession().getTitle();
				Editable text = (traceNameEditText == null ? null : traceNameEditText.getText());
				String newName = (text == null ? null : text.toString());
				if (newName != null) {
					sessionManager.getCurrentSession().setTitle(newName);
				}
				sessionManager.endCurrentSession();
				printTester("Trace \""+(newName==null?name:newName)+"\" was discarded! ");
				dismissDialog(TraceMap.PAUSE_DIALOG);
			}
		});
	}

	
	/**
	 * Helper method for setting up the save button on the pause dialog.
	 * 
	 * @param d the Dialog object for the pause Dialog.
	 */
	private void saveButtonHelper(Dialog d) {
		Button saveButton = (Button)d.findViewById(R.id.save_button);
		saveButton.setOnClickListener(new Button.OnClickListener(){
			public void onClick(View v) {
				try {
					String name = sessionManager.getCurrentSession().getTitle();
					Editable text = (traceNameEditText == null ? null : traceNameEditText.getText());
					String newName = (text == null ? null : text.toString());
					if (newName != null) {
						sessionManager.getCurrentSession().setTitle(newName);
					}
					pauseRecording();
					traceCache.writePointsToDb(sessionManager.getCurrentSession().getId()); // save the trace
					sessionManager.saveAndEndCurrentSession(mDb); // save the session
					liveOverlay.clearPoints();
					printTester("Trace \""+(newName==null?name:newName)+"\" was saved! ");
				} catch (Exception e) {
					if (DEBUGGING){printTester("saveHelper "+e.toString());}
					else {
						e.printStackTrace();
					}
				}
				dismissDialog(TraceMap.PAUSE_DIALOG);
			}
		});
	}

	
	/**
	 * Helper method for setting up the continue recording button on the pause dialog.
	 * @param d the Dialog object for the pause DIALOG
	 */
	private void continueButtonHelper(Dialog d) {
		Button continueButton = (Button)d.findViewById(R.id.continue_button);
		continueButton.setOnClickListener(new Button.OnClickListener(){
			public void onClick(View view) {
				try {
					startRecording();
				} catch (Exception e) {
					if (DEBUGGING){printTester("ContinueButtonHelper "+e.toString());}
					else {
						e.printStackTrace();
					}
				}
				dismissDialog(TraceMap.PAUSE_DIALOG);
			}
		});
	}
	
	
	//----------------------------------------------------
	// Code for getting location updates
	//----------------------------------------------------

	/**
	 * Stop recording locations
	 */
	private void pauseRecording() {
		recording = false;
		locationHelper.pauseLocationUpdates();
	}

	
	/**
	 * Start/restart recording locations
	 */
	private void startRecording() {
		recording = true;
		locationHelper.startLocationUpdates(); //TODO: Don't return a string here.
	}
	

	/**
	 * A method to handle updating the map when a new location is received.
	 * @param location the current location.
	 */
	public void updateLocation(Location location) {
		// crate a geopoint at the users' current location and display it on the map.
		if (location != null) {

			MapView mapView = (MapView)findViewById(R.id.mapview);
			GeoPoint point = new GeoPoint((int)(location.getLatitude()*1e6),
					(int)(location.getLongitude()*1e6));

			if (recording) {
				liveOverlay.addPoint(point);
				// Save trace point
				traceCache.cacheLocation(location);//saveTracePoint(location);
				mapView.getController().animateTo(point);
			}
		}
	}
	

	/**
	 * A method to encapsulate any work involved in getting the Location Listener
	 * @return the LocationListener to use 
	 */
	private LocationListener getLocationListener() {
		return new LocationListener() {
			public void onLocationChanged(Location location) {updateLocation(location);} // Just call the method to update location
			public void onProviderDisabled(String arg0) {/*TODO:Consider GPS turned off case.*/}
			public void onProviderEnabled(String arg0) {}
			public void onStatusChanged(String arg0, int arg1, Bundle arg2) {}
		};
	}

	
	//----------------------------------------//
	//  Code for Handling menu interactions   //
	//----------------------------------------//

	public boolean onPrepareOptionsMenu(Menu menu) {
		menu.clear();			
		boolean result = super.onCreateOptionsMenu(menu);
		menu.add(0, // Group id, (no group in this case) 
				LIST_ID, // a unique id
				0, // item order; 0 - no preference
				R.string.menu_list); // string resource for item.
		menu.add(1, // Group id, (no group in this case) 
				RECORD_ID, // a unique id
				0, // item order; 0 - no preference
				(this.recording ? R.string.menu_stop_record:R.string.menu_record)); // string resource for item.
		return result;
	}

	
	@Override
	/**
	 * Populate the options for the "Menu" button
	 */
	public boolean onCreateOptionsMenu(Menu menu) {
		boolean result = super.onCreateOptionsMenu(menu);
		menu.add(0, // Group id, (no group in this case) 
				LIST_ID, // a unique id
				0, // item order; 0 - no preference
				R.string.menu_list); // string resource for item.
		menu.add(1, // Group id, (no group in this case) 
				RECORD_ID, // a unique id
				0, // item order; 0 - no preference
				(this.recording ? R.string.menu_stop_record:R.string.menu_record)); // string resource for item.
		return result;
	}

	
	@Override
	/**
	 * Handle option menu events.
	 * 
	 * Consider looking for graphic refresh instead.
	 * 
	 */
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case RECORD_ID: 
			// Pausing the trace
			if (this.recording) {
				this.pauseRecording();
				this.showDialog(TraceMap.PAUSE_DIALOG);
			} else { // starting a new trace
				sessionManager.startNewSession(mDb); 
				this.startRecording();
			}	
			return true;
		case LIST_ID: 
			// Switch to list view
			Intent myIntent = new Intent(this.getApplicationContext(), TraceList.class);
			startActivityForResult(myIntent, 0);
			return true;
		}
		return super.onOptionsItemSelected(item);
	}
}